package de.jadehs.cosima.ui.converters;

import java.util.HashMap;
import java.util.Map;

import org.eclipse.core.runtime.Assert;
import org.eclipse.swt.SWT;
import org.eclipse.swt.widgets.Composite;
import org.eclipse.swt.widgets.Display;
import org.eclipse.zest.core.widgets.Graph;
import org.eclipse.zest.core.widgets.GraphConnection;
import org.eclipse.zest.core.widgets.GraphNode;
import org.eclipse.zest.core.widgets.IContainer;
import org.eclipse.zest.core.widgets.ZestStyles;
import org.eclipse.zest.layouts.LayoutStyles;
import org.eclipse.zest.layouts.algorithms.TreeLayoutAlgorithm;
import org.jgrapht.DirectedGraph;

import de.jadehs.cosima.AbstractParameterizedConverter;
import de.jadehs.cosima.CosimaUtil;
import de.jadehs.cosima.State;
import de.jadehs.cosima.Transition;
import de.jadehs.cosima.util.PrettyPrintUtil;

public class DirectedGraphToZestGraphConverter extends
		AbstractParameterizedConverter<DirectedGraph<State, Transition>, Graph> {
	@Override
	public Graph convert(DirectedGraph<State, Transition> f, Object... params) {
		Assert.isTrue(params != null && params.length == 1
				&& params[0] instanceof Composite,
				"Invalid parameter, expected Composite: " + params);
		return renderGraph((Composite) params[0], f);
	}

	public static Graph renderGraph(Composite parent,
			DirectedGraph<State, Transition> dgraph) {
		Graph graph = new Graph(parent, SWT.NONE);
		Map<State, GraphNode> nodes = new HashMap<State, GraphNode>();
		Map<String, GraphConnection> conns = new HashMap<String, GraphConnection>();

		for (State s : dgraph.vertexSet()) {
			GraphNode node = createGraphNode(graph, s);
			nodes.put(s, node);
		}

		for (Transition t : dgraph.edgeSet()) {
			GraphNode start = nodes.get(t.getStart());
			GraphNode end = nodes.get(t.getEnd());
			String key = t.getStart().getName() + "/" + t.getEnd().getName();

			GraphConnection conn = conns.get(key);

			if (conn == null) {
				conn = new GraphConnection(graph,
						ZestStyles.CONNECTIONS_DIRECTED, start, end);
				conns.put(key, conn);

				if (start == end) {
					conn.setCurveDepth(15);
				}

				conn.setLineColor(Display.getDefault()
						.getSystemColor(
								CosimaUtil.evaluateStartCondition(t,
										CosimaUtil.EPSILON) ? SWT.COLOR_GRAY
										: SWT.COLOR_BLACK));
				conn.setText((CosimaUtil.evaluateStartCondition(t,
						CosimaUtil.EPSILON) ? "eps" : PrettyPrintUtil
						.prettyPrintStartInstructions(t) + ""));
			} else {
				conn.setText(conn.getText() + "|"
						+ PrettyPrintUtil.prettyPrintStartInstructions(t));
			}

			String reversedKey = t.getEnd().getName() + "/"
					+ t.getStart().getName();
			GraphConnection reverseConn = conns.get(reversedKey);
			if (reverseConn != null) {
				conn.setCurveDepth(15);
				reverseConn.setCurveDepth(15);
			}
		}

		graph.setLayoutAlgorithm(new TreeLayoutAlgorithm(
				LayoutStyles.ENFORCE_BOUNDS
						| LayoutStyles.NO_LAYOUT_NODE_RESIZING), true);

		return graph;
	}

	private static GraphNode createGraphNode(IContainer graph, State s) {
		GraphNode node = new GraphNode(graph, SWT.None, s.getName());

		if (s.isStart()) {
			node.setBorderColor(Display.getDefault().getSystemColor(
					SWT.COLOR_BLACK));
			node.setBorderWidth(2);
		}

		switch (s.getType()) {
		case END:
			node.setBackgroundColor(Display.getDefault().getSystemColor(
					SWT.COLOR_GREEN));
			break;
		case UNDEF:
			node.setBackgroundColor(Display.getDefault().getSystemColor(
					SWT.COLOR_RED));
			break;
		}

		return node;
	}
}
